Bun supports workspaces
in package.json
. Workspaces make it easy to develop complex software as a monorepo consisting of several independent packages.
It's common for a monorepo to have the following structure:
tree
<root>
├── README.md
├── bun.lock
├── package.json
├── tsconfig.json
└── packages
├── pkg-a
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── pkg-b
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
└── pkg-c
├── index.ts
├── package.json
└── tsconfig.json
In the root package.json
, the "workspaces"
key is used to indicate which subdirectories should be considered packages/workspaces within the monorepo. It's conventional to place all the workspace in a directory called packages
.
{
"name": "my-project",
"version": "1.0.0",
"workspaces": ["packages/*"],
"devDependencies": {
"example-package-in-monorepo": "workspace:*"
}
}
Glob support — Bun supports full glob syntax in "workspaces"
(see here for a comprehensive list of supported syntax), except for exclusions (e.g. !**/excluded/**
), which are not implemented yet.
Each workspace has it's own package.json
. When referencing other packages in the monorepo, semver or workspace protocols (e.g. workspace:*
) can be used as the version field in your package.json
.
{
"name": "pkg-a",
"version": "1.0.0",
"dependencies": {
"pkg-b": "workspace:*"
}
}
bun install
will install dependencies for all workspaces in the monorepo, de-duplicating packages if possible. If you only want to install dependencies for specific workspaces, you can use the --filter
flag.
# Install dependencies for all workspaces starting with `pkg-` except for `pkg-c`
bun install --filter "pkg-*" --filter "!pkg-c"
# Paths can also be used. This is equivalent to the command above.
bun install --filter "./packages/pkg-*" --filter "!pkg-c" # or --filter "!./packages/pkg-c"
When publishing, workspace:
versions are replaced by the package's package.json
version,
"workspace:*" -> "1.0.1"
"workspace:^" -> "^1.0.1"
"workspace:~" -> "~1.0.1"
Setting a specific version takes precedence over the package's package.json
version,
"workspace:1.0.2" -> "1.0.2" // Even if current version is 1.0.1
Workspaces have a couple major benefits.
- Code can be split into logical parts. If one package relies on another, you can simply add it as a dependency in
package.json
. If packageb
depends ona
,bun install
will install your localpackages/a
directory intonode_modules
instead of downloading it from the npm registry. - Dependencies can be de-duplicated. If
a
andb
share a common dependency, it will be hoisted to the rootnode_modules
directory. This reduces redundant disk usage and minimizes "dependency hell" issues associated with having multiple versions of a package installed simultaneously. - Run scripts in multiple packages. You can use the
--filter
flag to easily runpackage.json
scripts in multiple packages in your workspace.
Share versions with Catalogs
When many packages need the same dependency versions, catalogs let you define those versions once in the root package.json
and reference them from your workspaces using the catalog:
protocol. Updating the catalog automatically updates every package that references it. See Catalogs for details.
⚡️ Speed — Installs are fast, even for big monorepos. Bun installs the Remix monorepo in about 500ms
on Linux.
- 28x faster than
npm install
- 12x faster than
yarn install
(v1) - 8x faster than
pnpm install
